package lmscript.postscript.syntax;

import lmscript.postscript.syntax.stack.OperandStack;

public abstract class DictionaryOperator extends PSOperator {

	public static Class[] operators = new Class[] { Dict.class, StartDict.class, EndDict.class,
			MaxLength.class, Begin.class, End.class, Def.class, Load.class, Store.class,
			Undef.class, Known.class, Where.class, CurrentDict.class, ErrorDict.class, Error.class,
			SystemDict.class, UserDict.class, GlobalDict.class, StatusDict.class,
			CountDictStack.class, DictStack.class, ClearDictStack.class };

	DictionaryOperator(String name) {
		super(name);
	}
}

class Dict extends DictionaryOperator {

	public Dict() {
		super("dict");
		operandTypes = new Class[] { PSInteger.class };
	}

	@Override
	public boolean execute(OperandStack os) {
		PSNumber n = os.popNumber();
		int initialCapacity = -1;
		if (n instanceof PSInteger) {
			initialCapacity = n.getNumber().intValue();
		}
		if (initialCapacity < 0) {
			throwError(os, new RangeCheck());
		} else {
			os.push(new PSDictionary(initialCapacity));
		}
		return true;
	}
}

class StartDict extends DictionaryOperator {

	public StartDict() {
		super("<<");
	}

	@Override
	public boolean execute(OperandStack os) {
		os.push(new PSMark());
		return true;
	}
}

class EndDict extends DictionaryOperator {

	public EndDict() {
		super(">>");
	}

	@Override
	public boolean execute(OperandStack os) {
		int len = os.countToMark();
		if (len < 0) {
			this.throwError(os, new UnmatchedMark());
		} else {
			if (os.count() < 2 || len % 2 != 0) {
				throwError(os, new RangeCheck());
			}
			len = len / 2;
			PSDictionary d = new PSDictionary(len);
			for (int i = 0; i < len; i++) {
				PSObject value = os.pop();
				PSObject key = os.pop();
				d.put(key, value);
			}
			os.pop();
			os.push(d);
		}
		return true;
	}
}

class MaxLength extends DictionaryOperator {

	public MaxLength() {
		super("maxlength");
		operandTypes = new Class[] { PSDictionary.class };
	}

	@Override
	public boolean execute(OperandStack os) {
		PSDictionary dict = os.popDict();
		os.pushNumber(dict.size());
		return true;
	}
}

class Begin extends DictionaryOperator {

	public Begin() {
		super("begin");
		operandTypes = new Class[] { PSDictionary.class };
	}

	@Override
	public boolean execute(OperandStack os) {
		os.getDictStack().push(os.popDict());
		return true;
	}
}

class End extends PackedArrayOperator {

	public End() {
		super("end");
	}

	@Override
	public boolean execute(OperandStack os) {
		if (os.getDictStack().pop() == null) {
            throwError(os, new DictStackUnderflow());
        }
        return true;
	}
}

class Def extends DictionaryOperator {

	public Def() {
		super("def");
		operandTypes = new Class[] {PSObject.class, PSObject.class};
	}

	@Override
	public boolean execute(OperandStack os) {
		PSObject value = os.pop();
        PSObject key = os.pop();
        PSDictionary d = os.getDictStack().getCurrentDictionary();
        d.put(key,value);
        return true;
	}
}

class Load extends DictionaryOperator {

	public Load() {
		super("load");
		operandTypes = new Class[] {PSObject.class};
	}

	@Override
	public boolean execute(OperandStack os) {
		PSObject key = os.pop();
        PSObject value = os.getDictStack().lookup(key);
        if (value == null) {
            throwError(os, new Undefined());
        } else {
            os.push(value);
        }
        return true;
	}
}

class Store extends DictionaryOperator {

	public Store() {
		super("store");
		operandTypes = new Class[] {PSObject.class, PSObject.class};
	}

	@Override
	public boolean execute(OperandStack os) {
		PSObject value = os.pop();
        PSObject key = os.pop();
        PSDictionary d = os.getDictStack().findDict(key);
        if (d == null) {
            os.getDictStack().getCurrentDictionary().put(key, value);
        } else {
            d.put(key, value);
        }
        return true;
	}
}

class Undef extends DictionaryOperator {

	public Undef() {
		super("undef");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class Known extends DictionaryOperator {

	public Known() {
		super("known");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class Where extends DictionaryOperator {

	public Where() {
		super("where");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class CurrentDict extends DictionaryOperator {

	public CurrentDict() {
		super("currentdict");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class ErrorDict extends DictionaryOperator {

	public ErrorDict() {
		super("errordict");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class Error extends DictionaryOperator {

	public Error() {
		super("$error");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class SystemDict extends DictionaryOperator {

	public SystemDict() {
		super("systemdict");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class UserDict extends DictionaryOperator {

	public UserDict() {
		super("userdict");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class GlobalDict extends DictionaryOperator {

	public GlobalDict() {
		super("globaldict");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class StatusDict extends DictionaryOperator {

	public StatusDict() {
		super("statusdict");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class CountDictStack extends DictionaryOperator {

	public CountDictStack() {
		super("countdictstack");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class DictStack extends DictionaryOperator {

	public DictStack() {
		super("dictstack");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}

class ClearDictStack extends DictionaryOperator {

	public ClearDictStack() {
		super("cleardictstack");
	}

	@Override
	public boolean execute(OperandStack os) {
		return false;
	}
}
